package org.example;


import org.example.annotation.EnableMyRpc;
import org.example.annotation.MyRpcClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;


//手动干预bean定义注册 自己的代理对象 和 处理器(这是因为要的东西要在容器里面拿 想要依赖注入)
//用于注册我rpc动态代理对象
@Component
public class MyRpcBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 获取到 BeanDefinitionRegistry，这样才能注册新的 BeanDefinition
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

        System.out.println("开始注册自定义bean");


        // Step 1: 获取 @EnableMyRpc 注解中的 basePackages 配置

        // 尝试从主应用类获取注解
        Class<?> mainClass = getMainApplicationClass();
        if (mainClass == null) {
            throw new RuntimeException("未找到主应用类");
        }

        EnableMyRpc enableMyRpc = AnnotationUtils.findAnnotation(mainClass, EnableMyRpc.class);

        //没有注解则不需要继续执行
        if (enableMyRpc == null) {
            return;
        }

        String[] basePackages = enableMyRpc.basePackages();


        // 如果 basePackages 为空，使用默认包
        if (basePackages.length == 0) {
            basePackages = new String[]{this.getClass().getPackage().getName()};
        }


        // Step 2: 扫描这些包，找到带有 @MyRpcClient 注解的接口类
        Set<Class<?>> myRpcClientInterfaces = new HashSet<>();
        for (String basePackage : basePackages) {
            Set<Class<?>> classes = scanPackage(basePackage);
            for (Class<?> clazz : classes) {
                if (clazz.isInterface() && clazz.isAnnotationPresent(MyRpcClient.class)) {
                    myRpcClientInterfaces.add(clazz);
                }
            }
        }


        // 对找到的接口类进行处理，创建代理对象 处理器和 代理对象都要注册bean
        for (Class<?> interfaceClass : myRpcClientInterfaces) {
            String beanName = StringUtils.uncapitalize(interfaceClass.getSimpleName());


            //注册处理器
            RpcInvocationHandler rpcInvocationHandler = new RpcInvocationHandler(beanName);
            registryMyHandler(rpcInvocationHandler, registry, beanName);


            //注册代理对象
            Object proxy = MyRpcFactory.createProxy(interfaceClass, rpcInvocationHandler);
            registryMyRpcProxy(interfaceClass, proxy, registry, beanName);

        }


    }

    //注册处理器 名字要是rpc处理器加上他代理的名字
    private void registryMyHandler(Object bean, BeanDefinitionRegistry registry, String beanName) {

        // 创建 BeanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(InvocationHandler.class);
        beanDefinition.setInstanceSupplier(() -> bean);

        // 注册 Bean 到 Spring 容器
        registry.registerBeanDefinition("myRpcHandler" + beanName, beanDefinition);

    }


    //注册rpc代理  名字是
    private void registryMyRpcProxy(Class<?> interfaceClass, Object bean, BeanDefinitionRegistry registry, String beanName) {

        // 创建 BeanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(interfaceClass);
        beanDefinition.setInstanceSupplier(() -> bean);

        // 注册 Bean 到 Spring 容器
        registry.registerBeanDefinition(beanName, beanDefinition);

    }


    //递归的  找到目录下的类
    private Set<Class<?>> findClasses(File directory, String packageName) {
        Set<Class<?>> classes = new HashSet<>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    classes.addAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    try {
                        String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                        classes.add(Class.forName(className));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return classes;
    }


    //扫描包  找到所有类
    private Set<Class<?>> scanPackage(String basePackage) {
        Set<Class<?>> classes = new HashSet<>();
        String path = basePackage.replace('.', '/');
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration<URL> resources = classLoader.getResources(path);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                if (resource.getProtocol().equals("file")) {
                    File directory = new File(resource.getFile());
                    if (directory.exists()) {
                        classes.addAll(findClasses(directory, basePackage));
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }


    //获取主类启动类
    private Class<?> getMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}
